<template>
  <div>
    <button type="button" class="btn btn-white btn-default btn-round" @click="selectFile()">
      <i class="ace-icon fa fa-upload"></i>
      {{text}}
    </button>

    <input class="hidden" :id="inputId + '-input'" ref="file" type="file" @change="uploadFile()">
  </div>
</template>

<script>
  export default {
    name: 'big-file',
    // 暴露出去的可配置的属性
    props: {
      text: {
        default: '上传大文件'
      },
      inputId: {
        default: 'file-upload'
      },
      suffixs: {
        default: []
      },
      use: {
        default: ''
      },
      shardSize: {
        default: 50 * 1024
      },
      url: {
        default: 'oss-append'
      },
      afterUpload: {
        type: Function,
        default: null
      }
    },
    data: function () {
      return {
      }
    },
    methods: {
      /**
       * 上传文件
       */
      uploadFile() {
        let _this = this
        //  下面这句等价于document.querySelector('#file-upload-input').files[0]
        let file = _this.$refs.file.files[0]
        //console.log(JSON.stringify(file))
        /*
          name: "test.mp4"
          lastModified: 1901173357457
          lastModifiedDate: Tue May 27 2099 14:49:17 GMT+0800 (中国标准时间) {}
          webkitRelativePath: ""
          size: 37415970
          type: "video/mp4"
        */

        //  生成文件标识, 标识多次上传的是不是同一个文件
        let key16 = hex_md5(file.name + file.size + file.type)  // 10进制
        let key10 = parseInt(key16, 16)  // 16进制
        let key62 = Tool._10to62(key10)  // 62进制, 经过转换可以让key的长度变短点
        //console.log(key, key10, key62)
        /*
          d41d8cd98f00b204e9800998ecf8427e
          2.8194976848941264e+38
          6sfSqfOwzmik4A4icMYuUe
         */

        // 判断文件格式
        let suffixs = _this.suffixs // 能上传的文件格式
        let fileName = file.name
        let suffix = fileName.substring(fileName.lastIndexOf('.') + 1, fileName.length).toLowerCase()
        let validateSuffix = false
        for (let i = 0; i < suffixs.length; i++) {
          if (suffixs[i].toLowerCase() === suffix) {
            validateSuffix = true
            break
          }
        }
        if (!validateSuffix) {
          Toast.warning('文件格式不正确! 只支持上传: ' + suffixs.join(', '))
          // 清空该input里的数据
          // 因为input标签绑定的是应该一个@change事件, 如果第一次上传.mp4文件, 会被成功拦截
          // 如果第二此继续上传该文件则不会拦截, 因为两次上传, input里的数据没有发生改变, 还是该文件
          $('#' + _this.inputId + '-input').val('')
          return
        }

        // 文件分片
        let shardSize = _this.shardSize  // 分片大小
        let shardIndex = 1  // 分片索引, 1表示第1个分片
        let size = file.size  // 文件的总大小
        let shardTotal = Math.ceil(size / shardSize)  // 分片总数, Math.ceil(x)返回大于等于参数x的最小整数, 即对浮点数向上取整.

        /*
        // FormData 对象的使用：
        //   1.用一些键值对来模拟一系列表单控件：即把form中所有表单元素的name与value组装成一个queryString
        //   2.异步上传二进制文件。
        let formData = new window.FormData()
        //  key: 'shard', 必须和后端controller参数名一致
        formData.append('shard', fileShard)
        formData.append('shardIndex', shardIndex)
        formData.append('shardSize', shardSize)
        formData.append('shardTotal', shardTotal)
        formData.append('name', file.name)
        formData.append('suffix', suffix)
        formData.append('size', size)
        formData.append('use', _this.use)
        formData.append('key', key62)
        Loading.show()
        _this.$axios.post(process.env.VUE_APP_SERVER + '/file/admin/upload', formData).then((resp) => {
          if (resp.data.success) {
            Loading.hide()

            console.log('上传文件成功!', resp.data)
            _this.afterUpload(resp)
            $('#' + _this.inputId + '-input').val('')
          }
        })
        */

        let param = {
          'shardIndex': shardIndex,
          'shardSize': shardSize,
          'shardTotal': shardTotal,
          'name': file.name,
          'suffix': suffix,
          'size': size,
          'use': _this.use,
          'key': key62
        }

        _this.check(param);
      },

      /**
       * 检查文件状态, 是否已经上传过, 上传到第几片了
       */
      check(param) {
        let _this = this
        _this.$axios.get(process.env.VUE_APP_SERVER + '/file/admin/check/' + param.key).then((resp) => {
          if (resp.data.success) {
            let obj = resp.data.content
            if (!obj) {
              param.shardIndex = 1
              console.log('没有找到文件记录, 从分片1开始上传')
              _this.upload(param)
            } else if (obj.shardIndex === obj.shardTotal) {
              Toast.success('文件极速秒传成功!')
              _this.afterUpload(resp)
              $('#' + _this.inputId + '-input').val('')
            } else {
              param.shardIndex = obj.shardIndex + 1
              console.log('找到文件记录, 从分片' + param.shardIndex + '开始上传')
              _this.upload(param)
            }
          } else {
            Toast.warning('文件上传失败')
            $('#' + _this.inputId + '-input').val('')
          }
        })
      },

      /**
       *  根据shardIndex, 上传分片数据
       */
      upload(param) {
        let _this = this
        let shardIndex = param.shardIndex
        let shardTotal = param.shardTotal
        let shardSize = param.shardSize
        let fileShard = _this.getFileShard(shardIndex, shardSize)
        //  将图片转为base64进行传输
        let fileReader = new FileReader()
        //  显示进度条
        Progress.show(parseInt((shardIndex - 1) * 100 / shardTotal))

        fileReader.onload = (e) => {
          param.shard = e.target.result  // base64

          _this.$axios.post(process.env.VUE_APP_SERVER + '/file/admin/' + _this.url, param).then((resp) => {
            if (resp.data.success) {
              console.log('上传文件成功!', resp.data)
              //  显示进度条
              Progress.show(parseInt(shardIndex * 100 / shardTotal))

              if (shardIndex < shardTotal) {
                //  上传下一个分片, 递归调用upload()
                param.shardIndex = param.shardIndex + 1
                _this.upload(param)
              } else {
                //  关闭进度条
                Progress.hide()

                _this.afterUpload(resp)

                $('#' + _this.inputId + '-input').val('')
              }
            }
          })
        }
        fileReader.readAsDataURL(fileShard)
      },

      /**
       *  获取分片数据
       */
      getFileShard(shardIndex, shardSize) {
        let _this = this
        let file = _this.$refs.file.files[0]
        let start = (shardIndex - 1) * shardSize  // 当前分片起始位置
        let end = Math.min(file.size, start + shardSize)  // 当前分片结束位置, 需要考虑文件大小的边界,
        return file.slice(start, end); // 从文件中截取当前的分片数据
      },

      /**
       * 点击上传文件按钮(触发上传功能)
       */
      selectFile() {
        let _this = this
        $('#' + _this.inputId + '-input').trigger('click')
      }
    }
  }
</script>

<style scoped>
</style>
